package cn.iocoder.yudao.module.ym.service.ticket;

import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.module.infra.enums.*;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApiImpl;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.ym.controller.admin.site.vo.SitePageReqVO;
import cn.iocoder.yudao.module.ym.controller.admin.site.vo.SiteRespVO;
import cn.iocoder.yudao.module.ym.dal.dataobject.maputil.AreaSiteUserDeptMap;
import cn.iocoder.yudao.module.ym.dal.dataobject.area.AreaDO;
import cn.iocoder.yudao.module.ym.dal.dataobject.site.SiteDO;
import cn.iocoder.yudao.module.ym.dal.dataobject.ticketlog.TicketLogDO;
import cn.iocoder.yudao.module.ym.dal.mysql.area.AreaMapper;
import cn.iocoder.yudao.module.ym.dal.mysql.site.SiteMapper;
import cn.iocoder.yudao.module.ym.dal.mysql.ticketlog.TicketLogMapper;
import cn.iocoder.yudao.module.ym.service.maputil.MapUtilService;
import cn.iocoder.yudao.module.ym.service.site.SiteService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import cn.iocoder.yudao.module.ym.controller.admin.ticket.vo.*;
import cn.iocoder.yudao.module.ym.dal.dataobject.ticket.TicketDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.ym.dal.mysql.ticket.TicketMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.TICKET_NOT_EXISTS;
import static cn.iocoder.yudao.module.infra.enums.TicketStatusEnum.PENDING;
import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
import static org.aspectj.bridge.MessageUtil.handleAll;

/**
 * 工单 Service 实现类
 *
 * @author 韩艳阳
 */
@Service
@Validated
public class TicketServiceImpl implements TicketService {

    @Resource
    private TicketMapper ticketMapper;

    @Resource
    private SiteMapper siteMapper;

    @Resource
    private SiteService siteService;

    @Resource
    private AreaMapper areaMapper;

    @Resource
    private TicketLogMapper ticketLogMapper;

    @Resource
    private AdminUserMapper adminUserMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Resource
    private NotifyMessageSendApiImpl notifyMessageSendApi;

    @Resource
    private WebProperties webProperties;

    @Resource
    private DictDataMapper dictDataMapper;

    @Resource
    private DeptMapper deptMapper;

    @Resource
    private MapUtilService mapUtilService;

    @Override
    public Map<String,Object> statistics(TicketPageReqVO pageReqVO) {
        List<TicketRespVO>  ticketList = BeanUtils.toBean(ticketMapper.selectList(pageReqVO), TicketRespVO.class);
        Map<String, Object> result = new LinkedHashMap<>();

        // 1. 工单状态统计（包含状态枚举映射）
        Map<String, Long> statusMap = ticketList.stream()
                .filter(t -> t.getTicketStatus() != null)
                .collect(Collectors.groupingBy(
                        t -> TicketStatusEnum.getDescByCode(t.getTicketStatus()),
                        Collectors.counting()
                ));

        // 2. 工单类型统计（包含类型枚举映射）
        Map<String, Long> typeMap = ticketList.stream()
                .filter(t -> t.getTicketType() != null)
                .collect(Collectors.groupingBy(
                        t -> TicketTypeEnum.getDescByCode(t.getTicketType()),
                        Collectors.counting()
                ));

        // 3. 结构化返回结果
        result.put("statusStatistics", completeStatusStats(statusMap));
        result.put("typeStatistics", completeTypeStats(typeMap));

        return result;
    }

    // 补全可能缺失的状态数据
    private Map<String, Long> completeStatusStats(Map<String, Long> rawMap) {
        Map<String, Long> stats = new LinkedHashMap<>();
        // 初始化所有状态
        Arrays.stream(TicketStatusEnum.values())
                .forEach(status -> stats.put(status.getDesc(), 0L));
        // 合并实际数据
        rawMap.forEach(stats::put);
        return stats;
    }

    // 补全可能缺失的类型数据
    private Map<String, Long> completeTypeStats(Map<String, Long> rawMap) {
        Map<String, Long> stats = new LinkedHashMap<>();
        Arrays.stream(TicketTypeEnum.values())
                .forEach(type -> stats.put(type.getDesc(), 0L));
        rawMap.forEach(stats::put);
        return stats;
    }

    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_CREATE_SUB_TYPE, bizNo = "{{0}}",
            success = YM_TICKET_CREATE_SUCCESS)
    public String createTicket(TicketSaveReqVO createReqVO) {
        //判断ids是否为空
        List<Long> siteIds = createReqVO.getSiteIds();
        if (siteIds != null && !siteIds.isEmpty()) {

            //锁
            ReentrantLock reentrantLock = new ReentrantLock();
            reentrantLock.lock();
            //获取ids
            List<TicketDO> ticketDOS = new ArrayList<>(siteIds.size());
            List<TicketLogDO> ticketLogDOS = new ArrayList<>(siteIds.size());
            Long loginUserDeptId = getLoginUserDeptId();

            // 1. 获取工单号
            long l = ticketMapper.selectCount(new LambdaQueryWrapper<TicketDO>().apply("DATE(create_time) = CURDATE()"));
            //  提取固定值到循环外部
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
            String format = LocalDate.now().format(formatter); // 日期前缀只计算一次
            //  工单编号优化：预生成前缀 + 高效计数器
            AtomicLong counter = new AtomicLong(l); // 线程安全计数器
            String numberPrefix = "WO-" + format + "-";
            //转换
            TicketDO ticket = BeanUtils.toBean(createReqVO, TicketDO.class);
            ticket.setDeptId(loginUserDeptId);

            // 3. 插入日志
            Long operatorId = createReqVO.getOperatorId();
            AdminUserDO adminUserDO = adminUserMapper.selectById(operatorId);

            try {
                for (Long siteId : siteIds) {

                    TicketDO ticketDO1 = new TicketDO();
                    BeanUtils.copyProperties(ticket, ticketDO1);

                    //插入部门id

                    // 2. 插入工单编号
                    // 工单编号生成优化：StringBuilder 替代格式化
                    long seq = counter.incrementAndGet();
                    String ticketNumber = new StringBuilder(numberPrefix)
                            .append(String.format("%05d", seq))
                            .toString();
                    ticketDO1.setTicketNumber(ticketNumber);
                    ticketDO1.setSiteId(siteId);
                    ticketDOS.add(ticketDO1);


                }
                if (!ticketDOS.isEmpty()) {
                    ticketMapper.insertBatch(ticketDOS);
                    //插入操作日志
                    LogRecordContext.putVariable("ticket", ticketDOS);

                    for (TicketDO ticketDO : ticketDOS) {
                        // 3. 插入日志
                        TicketLogDO ticketLogDO = new TicketLogDO();
                        ticketLogDO.setTicketId(ticketDO.getId());
                        ticketLogDO.setContent(String.format(TicketLogRecordConstants.CREATE_TICKET,getLoginUserNickname(),adminUserDO.getNickname()));
                        ticketLogDOS.add(ticketLogDO);
                    }
                    if (!ticketLogDOS.isEmpty()) {
                        ticketLogMapper.insertBatch(ticketLogDOS);
                    }

                }

                // 4. 发送站内信
                Map<String, Object> templateParams = new HashMap<>();
                templateParams.put("user", getLoginUserNickname());
                templateParams.put("count", siteIds.size());
                templateParams.put("type", TicketTypeEnum.getDescByCode(createReqVO.getTicketType()));
                templateParams.put("url", webProperties.getAdminUi().getUrl() + "/#/order/my");

                NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
                notifySendSingleToUserReqDTO.setUserId(createReqVO.getOperatorId());
                notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.SEND_TICKET_NOTICE.getSmsTemplateCode());
                notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
                notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                reentrantLock.unlock();
            }
            // 返回
            return "工单创建成功";
        }else {
            return "工单创建失败";
        }

    }


    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_UPDATE_SUCCESS)
    public void updateTicket(TicketSaveReqVO updateReqVO) {
        // 1.校验存在
        TicketDO oldTicketDO = validateTicketExists(updateReqVO.getId());
        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);
        // 3. 记录操作日志上下文
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldTicketDO, TicketSaveReqVO.class));
        LogRecordContext.putVariable("ticket", oldTicketDO);

        // 3.插入日志
        TicketLogDO ticketLogDO = new TicketLogDO();
        ticketLogDO.setTicketId(oldTicketDO.getId());
        ticketLogDO.setContent(String.format(TicketLogRecordConstants.UPDATE_TICKET,getLoginUserNickname()));
        ticketLogMapper.insert(ticketLogDO);


    }

    @Override
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_EDIT_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_EDIT_SUCCESS)
    public void editTicket(TicketSaveReqVO updateReqVO) {
        // 1.校验存在
        TicketDO oldSiteDO = validateTicketExists(updateReqVO.getId());
        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);


        //插入日志
        oldSiteDO.setTicketDesc(null);
        oldSiteDO.setTicketLevel(null);
        oldSiteDO.setTimeoutHour(null);

        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldSiteDO, TicketSaveReqVO.class));
        LogRecordContext.putVariable("ticket", oldSiteDO);

    }

    /**
     * 重新派发
     * @param updateReqVO 更新信息
     */
    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_REDISTRIBUTE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_REDISTRIBUTE_SUCCESS)
    public void redistribute(TicketSaveReqVO updateReqVO) {
        updateReqVO.setTicketStatus(PENDING.getCode()); //待处理
        updateReqVO.setRedistributeTime(LocalDateTime.now()); //重新派发时间
        // 1.校验存在
        TicketDO ticketDO = validateTicketExists(updateReqVO.getId());

        // 插入逻辑，如果重新派发时 原派发人 还未接收此工单 或者已经接收此工单，需要通知原派发人此工单已更换派发人
        Integer ticketStatus = ticketDO.getTicketStatus();
        if(ticketStatus == PENDING.getCode()
                || ticketStatus == TicketStatusEnum.PROCESSING.getCode()){
            Map<String, Object> templateParams = new HashMap<>();
            templateParams.put("ticketNum", ticketDO.getTicketNumber());

            NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
            notifySendSingleToUserReqDTO.setUserId(ticketDO.getOperatorId());
            notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.TICKET_REDISTRIBUTE.getSmsTemplateCode());
            notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
            notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);
        }

        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);
        //插入日志
        LogRecordContext.putVariable("ticket", ticketDO);
        // 3.插入日志
        TicketLogDO ticketLogDO = new TicketLogDO();
        ticketLogDO.setTicketId(ticketDO.getId());
        //获取重新派发人nickname
        AdminUserDO adminUserDO = adminUserMapper.selectById(updateReqVO.getOperatorId());
        if(adminUserDO == null){
            throw new RuntimeException("系统暂无该用户");
        }
        ticketLogDO.setContent(String.format(TicketLogRecordConstants.REDISTRIBUTE_TICKET,getLoginUserNickname(),adminUserDO.getNickname()));
        ticketLogMapper.insert(ticketLogDO);
        // 4. 发送站内信
        Map<String, Object> templateParams = new HashMap<>();
        templateParams.put("user", getLoginUserNickname());
        templateParams.put("type", TicketTypeEnum.getDescByCode(ticketDO.getTicketType()));
        templateParams.put("url", webProperties.getAdminUi().getUrl() + "/#/order/my");
        templateParams.put("count", 1);

        NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
        notifySendSingleToUserReqDTO.setUserId(updateReqVO.getOperatorId());
        notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.SEND_TICKET_NOTICE.getSmsTemplateCode());
        notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
        notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);
    }

    /**
     * 拒单
     * @param updateReqVO 更新信息
     */
    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_REFUSE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_REFUSE_SUCCESS)
    public void refuse(TicketSaveReqVO updateReqVO) {
        updateReqVO.setTicketStatus(TicketStatusEnum.REJECT.getCode()); //已拒绝
        // 1.校验存在
        TicketDO ticketDO = validateTicketExists(updateReqVO.getId());
        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);
        //插入日志
        LogRecordContext.putVariable("ticket", ticketDO);
        // 3. 插入日志
        TicketLogDO ticketLogDO = new TicketLogDO();
        ticketLogDO.setTicketId(ticketDO.getId());
        ticketLogDO.setContent(String.format(TicketLogRecordConstants.REFUSE_TICKET,getLoginUserNickname(),updateReqVO.getRefuseReason()));
        ticketLogMapper.insert(ticketLogDO);
        // 4. 发送站内信告诉发起人已经拒绝
        if(ticketDO.getThirdId() != TicketAddTypeEnum.TIMING.getCode()){
            Map<String, Object> templateParams = new HashMap<>();
            templateParams.put("user", getLoginUserNickname());
            templateParams.put("ticketNum", ticketDO.getTicketNumber());

            NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
            notifySendSingleToUserReqDTO.setUserId(Long.parseLong(ticketDO.getCreator()));
            notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.TICKET_REFUSE.getSmsTemplateCode());
            notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
            notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);
        }
    }

    /**
     * 接单
     * @param updateReqVO 更新信息
     */
    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_START_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_START_SUCCESS)
    public void takeOrders(TicketSaveReqVO updateReqVO) {
        updateReqVO.setTicketStatus(TicketStatusEnum.PROCESSING.getCode()); //处理中
        // 1.校验存在
        TicketDO ticketDO = validateTicketExists(updateReqVO.getId());
        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);
        //插入日志
        LogRecordContext.putVariable("ticket", ticketDO);
        // 3. 插入日志
        TicketLogDO ticketLogDO = new TicketLogDO();
        ticketLogDO.setTicketId(ticketDO.getId());
        ticketLogDO.setContent(String.format(TicketLogRecordConstants.TAKE_TICKET,getLoginUserNickname()));
        ticketLogMapper.insert(ticketLogDO);
        // 4. 发送站内信告诉发起人已经接单
        if(ticketDO.getThirdId() != TicketAddTypeEnum.TIMING.getCode()){
            Map<String, Object> templateParams = new HashMap<>();
            templateParams.put("user", getLoginUserNickname());
            templateParams.put("ticketNum", ticketDO.getTicketNumber());

            NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
            notifySendSingleToUserReqDTO.setUserId(Long.parseLong(ticketDO.getCreator()));
            notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.TICKET_RECEIVED.getSmsTemplateCode());
            notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
            notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);
        }

    }

    /**
     * 完单
     * @param updateReqVO 更新信息
     */
    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_END_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_END_SUCCESS)
    public void overOrders(TicketSaveReqVO updateReqVO) {
        updateReqVO.setTicketStatus(TicketStatusEnum.COMPLETED.getCode()); //已完成
        // 1.校验存在
        TicketDO ticketDO = validateTicketExists(updateReqVO.getId());
        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);
        //插入日志
        LogRecordContext.putVariable("ticket", ticketDO);
        // 3. 插入日志
        TicketLogDO ticketLogDO = new TicketLogDO();
        ticketLogDO.setTicketId(ticketDO.getId());
        ticketLogDO.setContent(String.format(TicketLogRecordConstants.OVER_TICKET,getLoginUserNickname()));
        ticketLogMapper.insert(ticketLogDO);
        // 4. 发送站内信告诉发起人已经完成
        if(ticketDO.getThirdId() != TicketAddTypeEnum.TIMING.getCode()){
            Map<String, Object> templateParams = new HashMap<>();
            templateParams.put("user", getLoginUserNickname());
            templateParams.put("ticketNum", ticketDO.getTicketNumber());

            NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
            notifySendSingleToUserReqDTO.setUserId(Long.parseLong(ticketDO.getCreator()));
            notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.TICKET_OVER.getSmsTemplateCode());
            notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
            notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);
        }

    }

    /**
     * 关闭订单
     * @param updateReqVO 更新信息
     */
    @Override
    @Transactional
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_CLOSE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
            success = YM_TICKET_CLOSE_SUCCESS)
    public void closeOrders(TicketSaveReqVO updateReqVO) {
        updateReqVO.setTicketStatus(TicketStatusEnum.CLOSED.getCode()); //已完成
        // 1.校验存在
        TicketDO ticketDO = validateTicketExists(updateReqVO.getId());
        // 2.更新
        TicketDO updateObj = BeanUtils.toBean(updateReqVO, TicketDO.class);
        ticketMapper.updateById(updateObj);
        //插入日志
        LogRecordContext.putVariable("site", ticketDO);
        // 3. 插入日志
        TicketLogDO ticketLogDO = new TicketLogDO();
        ticketLogDO.setTicketId(ticketDO.getId());
        ticketLogDO.setContent(String.format(TicketLogRecordConstants.CLOSE_TICKET,getLoginUserNickname()));
        ticketLogMapper.insert(ticketLogDO);
        // 4. 发送站内信告诉派发人已经关闭
        if(ticketDO.getThirdId() != TicketAddTypeEnum.TIMING.getCode()){
            Map<String, Object> templateParams = new HashMap<>();
            templateParams.put("user", getLoginUserNickname());
            templateParams.put("ticketNum", ticketDO.getTicketNumber());

            NotifySendSingleToUserReqDTO notifySendSingleToUserReqDTO = new NotifySendSingleToUserReqDTO();
            notifySendSingleToUserReqDTO.setUserId(ticketDO.getOperatorId());
            notifySendSingleToUserReqDTO.setTemplateCode(YmMessageEnum.TICKET_CLOSE.getSmsTemplateCode());
            notifySendSingleToUserReqDTO.setTemplateParams(templateParams);
            notifyMessageSendApi.sendSingleMessageToAdmin(notifySendSingleToUserReqDTO);
        }
    }

    @Override
    @LogRecord(type = YM_TICKET_TYPE, subType = YM_TICKET_DELETE_SUB_TYPE, bizNo = "{{#id}}",
            success = YM_TICKET_DELETE_SUCCESS)
    public void deleteTicket(Long id) {
        // 校验存在
        TicketDO ticketDO = validateTicketExists(id);
        // 删除
        ticketMapper.deleteById(id);
        //加入日志
        LogRecordContext.putVariable("site", ticketDO);
    }

    private TicketDO validateTicketExists(Long id) {
        TicketDO ticketDO = ticketMapper.selectById(id);
        if (ticketDO == null) {
            throw exception(TICKET_NOT_EXISTS);
        }
        return ticketDO;
    }

    @Override
    public TicketRespVO getTicket(Long id) {
        TicketRespVO ticketRespVO = BeanUtils.toBean(ticketMapper.selectById(id), TicketRespVO.class);
        Optional.ofNullable(ticketRespVO).ifPresent(a -> {
            a.setTicketLogDOS(ticketLogMapper.selectList(new LambdaQueryWrapper<TicketLogDO>().eq(TicketLogDO::getTicketId, a.getId())));
            SiteDO siteDO = siteMapper.selectById(a.getSiteId());
            Optional.ofNullable(siteDO).ifPresent(b -> {
                a.setSiteName(b.getSiteName());
                a.setSiteDO(b);
            });
            AreaDO areaDO = areaMapper.selectById(a.getAreaId());
            Optional.ofNullable(areaDO).ifPresent(b -> {
                a.setAreaName(b.getAreaName());
                a.setAreaDO(b);
            });
            AdminUserDO adminUserDO = adminUserMapper.selectById(a.getOperatorId());
            Optional.ofNullable(adminUserDO).ifPresent(b -> {
                a.setOperatorName(b.getNickname());
                a.setAdminUserDO(b);
            });
            List<TicketLogDO> ticketLogDOS = ticketLogMapper.selectList(new LambdaQueryWrapper<TicketLogDO>().eq(TicketLogDO::getTicketId, a.getId()));
            a.setTicketLogDOS(ticketLogDOS);
        });
        return ticketRespVO;
    }

    @Override
    public List<TicketRespVO> getMyCreateLise(TicketPageReqVO pageReqVO) {
        //获取我发起的工单
        Long loginUserId = getLoginUserId();
        pageReqVO.setCreator(String.valueOf(loginUserId));

        List<TicketRespVO>  ticketRespVOPageResult = BeanUtils.toBean(ticketMapper.selectList(pageReqVO), TicketRespVO.class);
        suppData(ticketRespVOPageResult);
        return ticketRespVOPageResult;
    }

    @Override
    public PageResult<TicketRespVO> getMyCreatePage(TicketPageReqVO pageReqVO) {
        //获取我发起的工单
        Long loginUserId = getLoginUserId();
        pageReqVO.setCreator(String.valueOf(loginUserId));

        PageResult<TicketRespVO>  ticketRespVOPageResult = BeanUtils.toBean(ticketMapper.selectPage(pageReqVO), TicketRespVO.class);
        List<TicketRespVO> list = ticketRespVOPageResult.getList();
        suppData(list);
        return ticketRespVOPageResult;
    }

    /**
     * 获取派给我的工单
     * @param pageReqVO 分页查询
     * @return
     */
    @Override
    public PageResult<TicketRespVO> getMyPage(TicketPageReqVO pageReqVO) {
        //获取派给我的工单
        Long loginUserId = getLoginUserId();
        pageReqVO.setOperatorId(loginUserId);

        PageResult<TicketRespVO>  ticketRespVOPageResult = BeanUtils.toBean(ticketMapper.selectPage(pageReqVO), TicketRespVO.class);
        List<TicketRespVO> list = ticketRespVOPageResult.getList();
        suppData(list);
        return ticketRespVOPageResult;
    }

    /**
     * 获取派给我的工单
     * @param pageReqVO 分页查询
     * @return
     */
    @Override
    public List<TicketRespVO> getMyList(TicketPageReqVO pageReqVO) {
        //获取派给我的工单
        Long loginUserId = getLoginUserId();
        pageReqVO.setOperatorId(loginUserId);

        List<TicketRespVO>  ticketRespVOPageResult = BeanUtils.toBean(ticketMapper.selectList(pageReqVO), TicketRespVO.class);
        suppData(ticketRespVOPageResult);
        return ticketRespVOPageResult;
    }


    @Override
    public PageResult<TicketRespVO> getTicketPage(TicketPageReqVO pageReqVO) {

        Long loginUserId = getLoginUserId();
        //查看当前用户是否有查看所有数据的权限
        Boolean flag = handleAll(loginUserId);

        PageResult<TicketRespVO> ticketRespVOPageResult = new PageResult<>();
        Set<Long> siteSet = new HashSet<>();
        //判断是不是管理员，只有管理员才可以查看所有的区域和电站，其它的只能查看自己负责的(有关的)
        if(flag){
            ticketRespVOPageResult = BeanUtils.toBean(ticketMapper.selectPage(pageReqVO), TicketRespVO.class);
        }else {
            //查看当前用户是否有查看所有电站的数据权限
            Boolean siteAll = handleSiteAll(loginUserId);

            AdminUserDO user = adminUserMapper.selectById(loginUserId);
            if(user != null){
                String siteIds = user.getSiteIds();
                if(siteIds != null && !siteIds.isEmpty()){
                    siteSet = Arrays.stream(siteIds.split(","))
                            .map(String::trim)
                            .map(Long::parseLong)    // 转为 Long
                            .collect(Collectors.toSet());
                }
            }
            if(!siteSet.isEmpty()){

                if(!siteAll){
                    SitePageReqVO sitePageReqVO = new SitePageReqVO();
                    sitePageReqVO.setIds(siteSet);
                    sitePageReqVO.setSiteStatus(DictDataConstants.YM_SITE_STATUS_5);
                    List<SiteDO> siteDOS = siteMapper.selectList(sitePageReqVO);
                    if(!siteDOS.isEmpty()){
                        siteSet =  siteDOS.stream().map(SiteDO::getId).collect(Collectors.toSet());
                    }
                }
                pageReqVO.setSiteIds(siteSet);
            }
            ticketRespVOPageResult = BeanUtils.toBean(ticketMapper.selectPage(pageReqVO), TicketRespVO.class);
        }

        List<TicketRespVO> list = ticketRespVOPageResult.getList();
        suppData(list);
        return ticketRespVOPageResult;
    }



    @Override
    public List<TicketRespVO> getTicketList(TicketPageReqVO pageReqVO) {
        Long loginUserId = getLoginUserId();
        //查看当前用户是否有查看所有数据的权限
        Boolean flag = handleAll(loginUserId);

        List<TicketRespVO> ticketRespVOList = new ArrayList<>();
        Set<Long> siteSet = new HashSet<>();
        //判断是不是管理员，只有管理员才可以查看所有的区域和电站，其它的只能查看自己负责的(有关的)
        if(flag){
            ticketRespVOList = BeanUtils.toBean(ticketMapper.selectList(pageReqVO), TicketRespVO.class);
        }else {
            //查看当前用户是否有查看所有电站的数据权限
            Boolean siteAll = handleSiteAll(loginUserId);

            AdminUserDO user = adminUserMapper.selectById(loginUserId);
            if(user != null){
                String siteIds = user.getSiteIds();
                if(siteIds != null && !siteIds.isEmpty()){
                    siteSet = Arrays.stream(siteIds.split(","))
                            .map(String::trim)
                            .map(Long::parseLong)    // 转为 Long
                            .collect(Collectors.toSet());
                }
            }
            if(!siteSet.isEmpty()){

                if(!siteAll){
                    SitePageReqVO sitePageReqVO = new SitePageReqVO();
                    sitePageReqVO.setIds(siteSet);
                    sitePageReqVO.setSiteStatus(DictDataConstants.YM_SITE_STATUS_5);
                    List<SiteDO> siteDOS = siteMapper.selectList(sitePageReqVO);
                    if(!siteDOS.isEmpty()){
                        siteSet =  siteDOS.stream().map(SiteDO::getId).collect(Collectors.toSet());
                    }
                }
                pageReqVO.setSiteIds(siteSet);
            }
            ticketRespVOList = BeanUtils.toBean(ticketMapper.selectList(pageReqVO), TicketRespVO.class);
        }




        suppData(ticketRespVOList);
        return ticketRespVOList;
    }


    /**
     * 填充list数据
     * @param list
     */
    private void suppData(List<TicketRespVO> list) {
        LocalDateTime now = LocalDateTime.now();
        if(list != null && !list.isEmpty()){
            //获取区域 电站 用户 部门的 map
            AreaSiteUserDeptMap map = mapUtilService.getFourMap();
            for (TicketRespVO ticketRespVO : list) {
                Integer ticketStatus = ticketRespVO.getTicketStatus();
                if(ticketStatus == TicketStatusEnum.PENDING.getCode() || ticketStatus == TicketStatusEnum.PROCESSING.getCode()){
                    LocalDateTime createTime = ticketRespVO.getCreateTime();
                    LocalDateTime redistributeTime = ticketRespVO.getRedistributeTime();
                    if(redistributeTime != null){
                        createTime = redistributeTime;
                    }
                    Integer timeoutHour = ticketRespVO.getTimeoutHour();
                    LocalDateTime localDateTime = createTime.plusHours(timeoutHour);
                    // 计算时间差
                    Duration duration = Duration.between(now, localDateTime);
                    double hoursDifference = duration.getSeconds() / 3600.0;
                    ticketRespVO.setHour(hoursDifference);
                }

                AdminUserDO adminUserDO = map.userMap.get(ticketRespVO.getOperatorId());
                if(adminUserDO != null){
                    ticketRespVO.setOperatorName(adminUserDO.getNickname());
                    ticketRespVO.setAdminUserDO(adminUserDO);
                }
                SiteDO siteDO = map.siteMap.get(ticketRespVO.getSiteId());
                if(siteDO != null){
                    ticketRespVO.setSiteName(siteDO.getSiteName());
                    ticketRespVO.setSiteDO(siteDO);
                }
                AreaDO areaDO = map.areaMap.get(ticketRespVO.getAreaId());
                if(areaDO != null){
                    ticketRespVO.setAreaName(areaDO.getAreaName());
                    ticketRespVO.setAreaDO(areaDO);
                }
                DeptDO deptDO = map.deptMap.get(ticketRespVO.getDeptId());
                if(deptDO != null){
                    ticketRespVO.setDeptName(deptDO.getName());
                }
            }
        }
    }



    /**
     * 判断当前用户是否 管理员
     * @param userId
     * @return
     */
    public Boolean handleAll(Long userId){
        //判断当前用户得权限  如果是tenant_admin 则查看全部数据
        List<UserRoleDO> userRoleDOS = userRoleMapper.selectListByUserId(userId);
        if(userRoleDOS!=null && !userRoleDOS.isEmpty()){
            for (UserRoleDO userRoleDO : userRoleDOS) {
                Long roleId = userRoleDO.getRoleId();
                RoleDO roleDO = roleMapper.selectById(roleId);
                if(roleDO!=null && roleDO.getCode().equals("tenant_admin")){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断当前用户是否 具有查看所有状态电站的权限
     * @param userId
     * @return
     */
    public Boolean handleSiteAll(Long userId){
        //2807	查看全部电站	yn:site:all
        //2808	查看已上线电站	ym:site_online
        List<UserRoleDO> userRoleDOS = userRoleMapper.selectListByUserId(userId);
        if(userRoleDOS != null && !userRoleDOS.isEmpty()){
            for (UserRoleDO userRoleDO : userRoleDOS) {
                Long roleId = userRoleDO.getRoleId();
                RoleDO roleDO = roleMapper.selectById(roleId);
                if(roleDO != null){
                    List<RoleMenuDO> roleMenuDOS = roleMenuMapper.selectListByRoleId(roleDO.getId());
                    if(roleMenuDOS != null && !roleMenuDOS.isEmpty()){
                        for (RoleMenuDO roleMenuDO : roleMenuDOS) {
                            if(roleMenuDO.getMenuId() == 2807){
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * 获取工单号
     * @return
     */
    private String getTicketNum() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        long l = ticketMapper.selectCount(new LambdaQueryWrapper<TicketDO>().apply("DATE(create_time) = CURDATE()"));
        return "WO-" + LocalDate.now().format(formatter) + "-" + String.format("%05d",Long.sum(1L, l));
    }


   /* //获取状态为商机的电站数量
    @Override
    public Integer pendingTicket(SitePageReqVO pageReqVO){

        List<SiteRespVO> siteList = siteService.getSiteList(pageReqVO);

        List<Long> siteIds = siteList.stream()
                .filter(a -> a !=null && a.getId() != null)
                .map(s -> s.getId())
                .collect(Collectors.toList());

        List<TicketDO> ticketDOS = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .eq(TicketDO::getOperatorId,getLoginUserId())
                .in(TicketDO::getSiteId,siteIds)
                .eq(TicketDO::getTicketStatus,PENDING));

        return ticketDOS.size();
    }*/

    @Override
    public Map<Object,Object> growthGate(SitePageReqVO pageReqVO1){

        Map<Object,Object> map = new HashMap<>();

        List<SiteRespVO> siteList = siteService.getSiteList(pageReqVO1);

        if (siteList == null || siteList.isEmpty()) {
            return null;
        }

        List<Long> siteIds = siteList.stream().map(a -> a.getId()).collect(Collectors.toList());
        //获取昨天的数据
        List<TicketDO> ticketDOListLast = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .in(TicketDO::getSiteId,siteIds)
                .apply("(TO_DAYS(NOW()) - TO_DAYS(create_time) = 1 or TO_DAYS(NOW()) - TO_DAYS(redistribute_time) = 1 )"));

        //今天的数据
        List<TicketDO> ticketDOListNow = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .in(TicketDO::getSiteId,siteIds)
                .apply("(TO_DAYS(create_time)=TO_DAYS(NOW()) or TO_DAYS(redistribute_time)=TO_DAYS(NOW()))"));


        int size1 = ticketDOListNow.size();
        int size2 = ticketDOListLast.size();

        double i = 0;
        if (size2 == 0) {
            i = size1;
        }else {
            i = (double) (size1 - size2) / size2;
        }
        map.put(3,size1);
        map.put(4,i);


        return map;
    }


    //工单状态数量统计和总数量
    public Map<Integer, Long> ticketStatistics(TicketPageReqVO pageReqVO){
        List<TicketRespVO> ticketList = getTicketList(pageReqVO);

        Map<Integer,Long> ticketMap = ticketList.stream()
                .filter(ticket -> ticket.getTicketStatus() >= 1 && ticket.getTicketStatus() <= 6)
                .collect(Collectors.groupingBy(
                        TicketRespVO::getTicketType,
                        Collectors.counting()
                ));

        Map<Integer,Long> end = new LinkedHashMap<>();

        for (int status = 1;status <= 6;status++)
        {
            end.put(status,ticketMap.getOrDefault(status,0L));
        }

        end.put(100, Long.valueOf(ticketMap.size()));

        return end;

    }

    //获取上月，前一年，总数据
    @Override
    public Map<Integer,List<TicketDO>> dateGrouping(TicketPageReqVO pageReqVO) {
        //获取到用户能看到的工单ids
        Map<Integer, List<TicketDO>> map = new HashMap<>();
        List<TicketRespVO> ticketList = getTicketList(pageReqVO);

        List<Long> ticketIds = ticketList.stream().map(a -> a.getId()).collect(Collectors.toList());
        //获取这些工单的上月数据，2025年5月获取到2025年4月
        List<TicketDO> ticketDOList1 = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .in(TicketDO::getId, ticketIds)
                .apply("create_time >= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-01')")
                .apply("create_time < DATE_FORMAT(CURRENT_DATE, '%Y-%m-01')"));
        //获取这些工单的去年上月数据，2025年5月获取到2024年4月
        List<TicketDO> ticketDOList2 = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .in(TicketDO::getId, ticketIds)
                .apply("YEAR(create_time) = YEAR(CURRENT_DATE) - 1"));
        //全部数据
        List<TicketDO> ticketDOList3 = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .in(TicketDO::getId, ticketIds));

        map.put(1,ticketDOList1);
        map.put(2,ticketDOList2);
        map.put(3,ticketDOList3);

        return map;
    }


    //站点详情的数据获取工单数据
    @Override
    public Map<Integer,List<TicketDO>> dateGrouping(Long id) {
        Map<Integer, List<TicketDO>> map = new HashMap<>();

        //获取这些工单的上月数据，2025年5月获取到2025年4月
        List<TicketDO> ticketDOList1 = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .eq(TicketDO::getSiteId, id)
                .apply("create_time >= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-01')")
                .apply("create_time < DATE_FORMAT(CURRENT_DATE, '%Y-%m-01')"));
        //获取这些工单的去年上月数据，2025年5月获取到2024年4月
        List<TicketDO> ticketDOList2 = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .eq(TicketDO::getSiteId, id)
                .apply("YEAR(create_time) = YEAR(CURRENT_DATE) - 1"));
        //全部数据
        List<TicketDO> ticketDOList3 = ticketMapper.selectList(new LambdaQueryWrapper<TicketDO>()
                .eq(TicketDO::getSiteId, id));

        map.put(1,ticketDOList1);
        map.put(2,ticketDOList2);
        map.put(3,ticketDOList3);

        return map;
    }

    //获取状态为待处理的工单数量和用户能看到的工单的总数量
    @Override
    public Map<Integer,Integer> ticketMetrics(TicketPageReqVO pageReqVO) {
        Map<Integer,Integer> map = new LinkedHashMap<>();


        //获取当前用户能看到的电站的所有工单
        PageResult<TicketRespVO> ticketPage = getTicketPage(pageReqVO);
        List<TicketRespVO> list = ticketPage.getList();

        int i = 0;
        //获取工单状态为待处理的数量
        for (TicketRespVO ticketRespVO : list) {
            if (ticketRespVO != null) {
                if(ticketRespVO.getTicketStatus().equals(PENDING.getCode()))
                {
                    i++;
                }
            }
        }

        map.put(1,i);
        map.put(2,list.size());

        return map;
    }

    //获取某人全部工单数统计
    @Override
    public List<Map<Long, Object>> allCount(){
        return ticketMapper.allCount();
    }
    //获取今天的某人工单数的统计
    @Override
    public List<Map<Long, Object>> todayCount(){
        return ticketMapper.todayCount();
    }
    //获取某人每一天的工单统计
    @Override
    public PageResult<Map<String, Object>> getDailyTicketCountByOperator(Long operatorId,  Integer pageNo, Integer pageSize){

        //计算某一页的开始数
        pageNo =  (pageNo - 1) * pageSize;
        //获取到没人每天的数据(分页)
        List<Map<String, Object>> dailyTicketCountByOperator = ticketMapper.getDailyTicketCountByOperatorEnd(operatorId,pageNo,pageSize);
        //获取到没人每天的数据(不分页),用来获取total
        List<Map<String, Object>> dailyTicketCountByOperatorList = ticketMapper.getDailyTicketCountByOperatorListEnd(operatorId);


        //转换为map并返回，对获取的date进行格式转换和获取它是周几
        SimpleDateFormat sdf = new SimpleDateFormat("E", Locale.CHINA);
        //数据格式转换
        for (Map<String, Object> map : dailyTicketCountByOperator) {

            Date o = (Date) map.get("endDate");
            map.replace("endDate",o + "-" + sdf.format(o));


            Object o1 = map.get("dailyCount");
            map.replace("dailyCount",o1.toString() + " " + "单");
        }

        PageResult pageResult = new PageResult<>();

        pageResult.setList(dailyTicketCountByOperator);
        pageResult.setTotal((long) dailyTicketCountByOperatorList.size());

        return pageResult;
    }

    @Override
    public List<Map<String, Object>> getDailyTicketCountByOperator(Long operatorId) {
        //获取到没人每天的数据(不分页),用来获取total
        List<Map<String, Object>> dailyTicketCountByOperatorList = ticketMapper.getDailyTicketCountByOperatorList(operatorId);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (Map<String, Object> map : dailyTicketCountByOperatorList) {
            Object value = map.get("endDate");
            if (value == null || value.equals("")) {
                continue;
            }
            if (value instanceof Long) {
                Date date = new Date((Long) value);
                String string = sdf.format(date);
                map.put("end_date", string); // 覆盖原值

            } else if (value instanceof String) {
                // 如果时间戳是字符串格式（如"1661990400000"）
                try {
                    long timestamp = Long.parseLong((String) value);
                    Date date = new Date(timestamp);
                    String string = sdf.format(date).toString();
                    map.put("end_date",string );
                } catch (NumberFormatException e) {
                    // 处理转换异常
                }
            }

        }
        return dailyTicketCountByOperatorList;
    }

    /**
     * 获取某人待处理的工单数量
     * @param userId
     * @return
     */
    @Override
    public long countByUserIdAndStatus(Long userId,Integer type) {
        return ticketMapper.selectCount(new LambdaQueryWrapper<TicketDO>()
                .eq(TicketDO::getTicketStatus, type)
                .eq(TicketDO::getOperatorId,userId));
    }

    /**
     * 获取某人今天的工单数量
     * @param userId
     * @return
     */
    @Override
    public long todayCountByUserId(Long userId) {
        return  ticketMapper.selectCount(new LambdaQueryWrapper<TicketDO>()
                .eq(TicketDO::getOperatorId,userId)
                .apply("DATE(create_time) = CURDATE()"));
    }


    @Override
    public TicketTypeRespVO ticketTypeCount(@Param("sTime") LocalDate sTime,@Param("operatorId") Long operatorId){
        //获取当天的开始和结束时间
        LocalDateTime start = sTime.atStartOfDay();

        LocalDateTime end = sTime.atTime(23, 59, 59, 999999999);
        LocalDateTime[] localDateTimes = new LocalDateTime[]{start,end};

        //获取当天人员
        TicketPageReqVO ticketPageReqVO = new TicketPageReqVO();
        ticketPageReqVO.setOperatorId(operatorId);
        ticketPageReqVO.setEndTime(localDateTimes);
        List<TicketDO> ticketDOS = ticketMapper.selectList(ticketPageReqVO);

        List<TicketRespVO> bean = BeanUtils.toBean(ticketDOS, TicketRespVO.class);

        for (TicketRespVO ticketRespVO : bean) {
            Long siteId = ticketRespVO.getSiteId();
            if (siteId != null) {
                SiteDO siteDO = siteMapper.selectOne(new LambdaQueryWrapper<SiteDO>()
                        .eq(SiteDO::getId, siteId)
                        .last("LIMIT 1"));
                String siteName = siteDO.getSiteName();
                if (siteName != null) {
                    ticketRespVO.setSiteName(siteName);
                }
            }
        }

        TicketTypeRespVO result = new TicketTypeRespVO();
        result.setInspection(
                bean.stream()
                        .filter(t -> t.getTicketType() == 1) // 1=巡检
                        .collect(Collectors.toList())
        );

        result.setMaintenance(
                bean.stream()
                        .filter(t -> t.getTicketType() == 2) // 2=保养
                        .collect(Collectors.toList())
        );

        result.setRepair(
                bean.stream()
                        .filter(t -> t.getTicketType() == 3) // 3=维修
                        .collect(Collectors.toList())
        );

        return result;

    }

}
